﻿/*
 * Copyright 2011 Hanns Kucer
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Threading;
using System.Net.Sockets;

namespace iTachToolkit
{
    internal abstract class Tube
    {
        protected abstract System.IO.Stream Stream
        {
            get;
        }

        public abstract bool IsConnected
        {
            get;
        }

        public abstract void Disconnect();

        protected Tube()
        {
        }

        private System.IO.StreamWriter s;

        /// <summary>
        /// Synkron skrivning
        /// </summary>
        /// <returns></returns>
        public virtual void Send(string data)
        {
            if (s == null)
                s = new System.IO.StreamWriter(Stream);
            s.Write(data);
            s.Write("\r");
            s.Flush();
        }

        /// <summary>
        /// Synkron läsning
        /// </summary>
        /// <returns></returns>
        public virtual string Receive()
        {
            var str = new System.Text.StringBuilder();
            char c;

            while (true)
            {
                c = (char)Read();
                if (c == '\r') break;
                str.Append(c);
            }

            return str.ToString();
        }

		public class ConnectionLostException : Exception
		{
			public ConnectionLostException()
				: base("Connection Lost!")
			{
			}
		}

        // TODO: Implement internal buffer to read more than 1 byte at a time
        private byte Read()
        {
            var b = new byte[1];
            lock (this)
            {
				AsyncCallback cbDone = (IAsyncResult r) =>
				{
					lock (this)
						Monitor.Pulse(this);
				};

				IAsyncResult ar = Stream.BeginRead(b, 0, 1, cbDone, null);
                while (!ar.IsCompleted)
                {
                    // if wait returned before timeout without being completed,
                    // then we are very likely to have lost the connection
                    if (Monitor.Wait(this, 2000) && !ar.IsCompleted)
						throw new ConnectionLostException();
                }
                return b[0];
            }
        }

        ~Tube()
        {
			try
			{
				if (s != null) s.Close();
				Disconnect();
			}
			catch
			{
				// do not throw in destructor
			}
        }
    }
}